﻿using Microsoft.EntityFrameworkCore;
using Vote.Project001.Core.Database.Model;
using Vote.Project001.Core.Infrastructure;

namespace Vote.Project001.Core.Framework
{
    public class HeroManager: Singleton<HeroManager>
    {
        private readonly List<Hero> _heroes = new List<Hero>();
        private readonly Dictionary<string, Hero> _idToHeroDictionary = new Dictionary<string, Hero>();
        private readonly object _lock = new object();

        protected override void OnInitialize()
        {
            using var dbContext = new Database.DatabaseContext();
            lock (_lock)
            {
                _heroes.Clear();
                _idToHeroDictionary.Clear();

                _heroes.AddRange(dbContext.Heroes.AsNoTracking().ToList());
                foreach (var hero in _heroes)
                {
                    _idToHeroDictionary.Add(hero.Id, hero);
                }
            }
        }

        public void Reload(string dataPath)
        {
            lock (_lock)
            {
                var heroes = FastJSON.JSON.ToObject<List<Hero>>(File.ReadAllText(dataPath, System.Text.Encoding.UTF8));

                using var dbContext = new Database.DatabaseContext();

                var transaction = dbContext.Database.BeginTransaction();

                dbContext.Votes.ExecuteDelete();
                dbContext.Heroes.ExecuteDelete();
                dbContext.SaveChanges();

                transaction.Commit();

                transaction = dbContext.Database.BeginTransaction();

                dbContext.Heroes.AddRange(heroes);
                dbContext.SaveChanges();

                transaction.Commit();
            }

            OnInitialize();
        }

        public void Update(string dataPath)
        {
            lock (_lock)
            {
                var heroes = FastJSON.JSON.ToObject<List<Hero>>(File.ReadAllText(dataPath, System.Text.Encoding.UTF8));

                using var dbContext = new Database.DatabaseContext();

                var transaction = dbContext.Database.BeginTransaction();

                foreach (var hero in heroes)
                {
                    dbContext.Heroes.Where(t => t.Id == hero.Id).UpdateFromQuery(t => new Hero()
                    {
                        AvatarPaths = hero.AvatarPaths, Introduction = hero.Introduction, Name = hero.Name,
                        Department = hero.Department, CategoryId = hero.CategoryId
                    });
                }

                dbContext.SaveChanges();

                transaction.Commit();
            }

            OnInitialize();
        }

        /// <summary>
        /// Get heroes list.
        /// </summary>
        /// <remarks>
        /// DO not Modify return values which will not be saved to database.
        /// </remarks>
        /// <returns></returns>
        public List<Hero> GetHeroes()
        {
            return _heroes;
        }

        public Hero? GetHero(string heroId)
        {
            lock (_lock)
            {
                return _idToHeroDictionary.ContainsKey(heroId) ? _idToHeroDictionary[heroId] : null;
            }
        }

        public List<Model.Response.HeroInformation> ListHeroWithVoteCount(string sessionId)
        {
            lock (_lock)
            {
                var result = new List<Model.Response.HeroInformation>();

                var session = SessionManager.Instance.GetSession(sessionId);

                if (session == null)
                    throw new InvalidOperationException("Session not created.");

                var userVoteList = VoteManager.Instance.GetUserVoteList(session.UserId);

                foreach (var hero in _heroes)
                {
                    var hi = new Model.Response.HeroInformation()
                    {
                        AvatarPaths = hero.AvatarPaths,
                        CategoryId = hero.CategoryId,
                        Department = hero.Department,
                        Id = hero.Id,
                        Introduction = hero.Introduction,
                        Name = hero.Name
                    };

                    hi.VoteCount = VoteManager.Instance.GetHeroVoteCount(hi.Id);

                    hi.Voted = userVoteList.Contains(hero.Id);

                    result.Add(hi);
                }

                return result;
            }
        }
    }
}
